package webjoch.process.strategies.puzzles;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;

import com.rsbuddy.event.events.MessageEvent;
import com.rsbuddy.script.methods.Calculations;
import com.rsbuddy.script.methods.Camera;
import com.rsbuddy.script.methods.Objects;
import com.rsbuddy.script.methods.Players;
import com.rsbuddy.script.methods.Widgets;
import com.rsbuddy.script.task.Task;
import com.rsbuddy.script.util.Filter;
import com.rsbuddy.script.wrappers.GameObject;
import com.rsbuddy.script.wrappers.Tile;

import webjoch.Main;
import webjoch.misc.Functions;
import webjoch.process.strategies.Puzzle;
import webjoch.wrappers.Room;
/**
 * 
 * @author Webjoch
 *
 */
public class Maze extends Puzzle{
	Room room;
	private final int LEVER = 49351;
	private final int LOCKED_CHEST = 49345;
	private final int BARRIERS[] = {49344, 49341};
	
	public Tile lastTile = null;
	
	private ArrayList<GameObject> passedBarriers = new ArrayList<GameObject>();
	private ArrayList<GameObject> ignoreBarriers = new ArrayList<GameObject>();
	GameObject lastBarrier;
	
	private int currentRing = -1;
	
	@Override
	public void setRoom(Room room) {
		this.room = room;
	}

	public GameObject getLockedChest(){
		GameObject[] lockedChest = Objects.getLoaded(new Filter<GameObject>(){
			public boolean accept(GameObject o){
				return o.getId() == LOCKED_CHEST && room.getArea().contains(o.getLocation());
			}
		});
		if (lockedChest.length == 0) return null;
		return lockedChest[0];
	}
	
	@Override
	public boolean isValid() {
		return getLockedChest() != null;
	}

	@Override
	public Type getType() {
		return Type.MAZE;
	}

	@Override
	public int loop(){
		if (Camera.getPitch() < 60 || Camera.getPitch() > 80){
			Camera.setPitch(70);
		}
		Tile local = Players.getLocal().getLocation();
		
		if (getLockedChest().getLocation().distanceTo() < 3){
			getLockedChest().interact("Open");
			Task.sleep(2000);
			return 1;
		}
		
		if (passedBarriers.size() > 0){
			if (currentRing < 0) currentRing = getRing(local);
			
			if (Math.abs(getRing(passedBarriers.get(0).getLocation()) - currentRing) > 1){
				passedBarriers.clear();
			}
			
			GameObject lever = room.getObject(LEVER);
			if (lever != null && !Widgets.get(945).isValid()){
				lever.interact("Pull");
				for (int i = 0; i < 100 && !Widgets.get(945).isValid(); i++){
					Task.sleep(25);
					if (Players.getLocal().isMoving()) i = 0;
				}
				return 1;
			}
			if (currentRing != getRing(local)){
				passedBarriers.remove(0);
				currentRing = getRing(local);
			}
			GameObject nextBarrier = passedBarriers.get(0);
			GameObject secondBarrier = passedBarriers.get(1);
			
			for (GameObject ringBarrier : getReachedBarriers(local)){
				if (ringBarrier.getLocation().equals(secondBarrier.getLocation())){
					passedBarriers.remove(0);
					return 1;
				}
			}
			
			if (nextBarrier != null){
				Camera.setCompass(sideToDirection(getSide(nextBarrier.getLocation())));
				if (!nextBarrier.isOnScreen()){
					nextBarrier.getLocation().getLocation().clickOnMap();
					for (int i = 0; i < 100 && nextBarrier.getLocation().distanceTo() > 4; i++){
						Task.sleep(25);
						if (Players.getLocal().isMoving()) i = 0;
					}
				}else{
					if (nextBarrier.interact("Go-through")){
						for (int i = 0; i < 100 && currentRing == getRing(Players.getLocal().getLocation()); i++){
							Task.sleep(13);
							if (Players.getLocal().isMoving()) i = 0;
						}
					}
				}
			}
		}else{
			boolean done = findPath(local, false, getLockedChest().getLocation(), true);
			if (!done){
				Main.getDungeon().ignoreRoom(room);
				ignoreBarriers.clear();
			}
		}
		return 0;
	}

	@Override
	public int[] itemNeed() {
		return new int[0];
	}

	@Override
	public boolean isFinished() {
		return false;
	}
	
	public boolean findPath(Tile tile, boolean traverse, Tile destination, boolean passBarriers){
		if (Calculations.distanceBetween(destination, tile) < 2) return true;
				
		if (passBarriers){
			lastTile = tile;
			for (GameObject barrier : getBarriers(getRing(tile))){
				Tile tileBehind = getTileBehind(tile);
				
				if (!alreadyPassed(barrier)){
					Tile nextTile = null;
					if (barrier.getLocation().equals(tile)){
						if (getSide(tile) == 0) nextTile = new Tile(tile.getX(), tile.getY()-1);
						if (getSide(tile) == 1) nextTile = new Tile(tile.getX(), tile.getY()+1);
						if (getSide(tile) == 2) nextTile = new Tile(tile.getX()+1, tile.getY());
						if (getSide(tile) == 3) nextTile = new Tile(tile.getX()-1, tile.getY());
					}
					if (barrier.getLocation().equals(tileBehind)){
						nextTile = tileBehind;
					}
					
					if (barrier.getLocation().equals(tile) || barrier.getLocation().equals(tileBehind)){
						lastBarrier = barrier;
						passedBarriers.add(barrier);
						if (findPath(nextTile, traverse, destination, true)) return true;
					}
				}
			}
		}
		
		for (GameObject obstacle : getObstacles(getRing(tile))){
			if (obstacle.getLocation().equals(tile)){
				if (traverse){
					if (!passBarriers) return false;
					passedBarriers.clear();
					ignoreBarriers.add(lastBarrier);
					return findPath(Players.getLocal().getLocation(), false, destination, passBarriers);
				}else{
					System.out.println("Traverse");
					traverse = true;
				}
			}
		}
		
		Tile nextTile = null;
		if (!traverse){
			if (getSide(tile) == 0) nextTile = new Tile(tile.getX()+1, tile.getY());
			if (getSide(tile) == 1) nextTile = new Tile(tile.getX()-1, tile.getY());
			if (getSide(tile) == 2) nextTile = new Tile(tile.getX(), tile.getY()+1);
			if (getSide(tile) == 3) nextTile = new Tile(tile.getX(), tile.getY()-1);
			
			if (getRing(nextTile) != getRing(tile)){
				if (getSide(tile) == 0) nextTile = new Tile(tile.getX(), tile.getY()-1);
				if (getSide(tile) == 1) nextTile = new Tile(tile.getX(), tile.getY()+1);
				if (getSide(tile) == 2) nextTile = new Tile(tile.getX()+1, tile.getY());
				if (getSide(tile) == 3) nextTile = new Tile(tile.getX()-1, tile.getY());
			}
		}else{
			if (getSide(tile) == 0) nextTile = new Tile(tile.getX()-1, tile.getY());
			if (getSide(tile) == 1) nextTile = new Tile(tile.getX()+1, tile.getY());
			if (getSide(tile) == 2) nextTile = new Tile(tile.getX(), tile.getY()-1);
			if (getSide(tile) == 3) nextTile = new Tile(tile.getX(), tile.getY()+1);
			
			if (getRing(nextTile) != getRing(tile)){
				if (getSide(tile) == 0) nextTile = new Tile(tile.getX(), tile.getY() - 1);
				if (getSide(tile) == 1) nextTile = new Tile(tile.getX(), tile.getY() + 1);
				if (getSide(tile) == 2) nextTile = new Tile(tile.getX() - 1, tile.getY());
				if (getSide(tile) == 3) nextTile = new Tile(tile.getX() + 1, tile.getY());
			}
		}
		return findPath(nextTile, traverse, destination, passBarriers);
	}
	
	public GameObject[] getReachedBarriers(Tile tile){
		ArrayList<GameObject> barriers = new ArrayList<GameObject>();
		
		for (GameObject barrier : getBarriers(getRing(tile))){
			if (findPath(tile, false, barrier.getLocation(), false)) barriers.add(barrier);
		}
		return barriers.toArray(new GameObject[barriers.size()]);
	}
	
	public Tile getTileBehind(Tile tile){
		Tile tileBehind = null;
		if (getSide(tile) == 0) tileBehind = new Tile(tile.getX(), tile.getY()+1);
		if (getSide(tile) == 1) tileBehind = new Tile(tile.getX(), tile.getY()-1);
		if (getSide(tile) == 2) tileBehind = new Tile(tile.getX()-1, tile.getY());
		if (getSide(tile) == 3) tileBehind = new Tile(tile.getX()+1, tile.getY());
		return tileBehind;
	}
	
	public boolean alreadyPassed(GameObject barrier){
		for (GameObject oneBarrier : passedBarriers){
			if (oneBarrier == null) continue;
			if (oneBarrier.getLocation().equals(barrier.getLocation())) return true;
		}
		for (GameObject oneBarrier : ignoreBarriers){
			if (oneBarrier == null) continue;
			if (oneBarrier.getLocation().equals(barrier.getLocation())) return true;
		}
		return false;
	}
	
	public GameObject[] getBarriers(final int ring){
		return Objects.getLoaded(new Filter<GameObject>(){
			public boolean accept(GameObject o){
				return Functions.inArray(BARRIERS, o.getId()) && room.getArea().contains(o.getLocation()) && (getRing(o.getLocation()) == ring || getRing(o.getLocation()) == ring - 1);
			}
		});
	}
	
	public GameObject[] getObstacles(final int ring){
		return Objects.getLoaded(new Filter<GameObject>(){
			public boolean accept(GameObject o){
				return o.getType() == GameObject.Type.INTERACTIVE && room.getArea().contains(o.getLocation()) && getRing(o.getLocation()) == ring;
			}
		});
	}
	
	public int[] getDistances(Tile tile){
		Tile tile1 = room.getArea().getTileArray()[room.getArea().getTileArray().length-1];
		Tile tile2 = room.getArea().getTileArray()[0];
		int[] distances = new int[4];
		distances[0] = (int) Calculations.distanceBetween(tile, new Tile (tile1.getX() + (tile.getX() - tile1.getX()), tile1.getY())); //y1
		distances[1] = (int) Calculations.distanceBetween(tile, new Tile (tile2.getX() + (tile.getX() - tile2.getX()), tile2.getY())); //y2
		distances[2] = (int) Calculations.distanceBetween(tile, new Tile (tile2.getX(), tile2.getY() + (tile.getY() - tile2.getY()))); //x1
		distances[3] = (int) Calculations.distanceBetween(tile, new Tile (tile1.getX(), tile1.getY() + (tile.getY() - tile1.getY()))); //x2
		return distances;
	}
	
	public int getRing(final Tile tile){
		int[] distances = getDistances(tile);
		
		int lowest = 500;
		for (int distance : distances){
			if (distance < lowest) lowest = distance;
		}
		return lowest;
	}
	
	public int getSide(Tile tile){
		int[] distances = getDistances(tile);
		int lowest = 500;
		int side = 0;
		for (int i = 0; i < distances.length; i++){
			if (distances[i] < lowest){
				lowest = distances[i];
				side = i;
			}
		}
		return side;
	}

	public char sideToDirection(int side){
		switch(side){
		case 0: return 'n';
		case 1: return 's';
		case 2: return 'e';
		case 3: return 'e';
		}
		return 'n';
	}
	
	@Override
	public void messageReceived(MessageEvent e) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onRepaint(Graphics2D g) {
		if (lastTile != null){
			lastTile.draw(g);
		}
		if (passedBarriers.size() > 0){
			int i = 0;
			for (GameObject barrier: passedBarriers){
				i++;
				if (barrier != null){
					barrier.draw(g);
					g.setColor(Color.RED);
					g.drawString("N: "+ i, barrier.getLocation().getCenterPoint().x, barrier.getLocation().getCenterPoint().y);
				}
			}
		}
	}
}













